package myalg.diningphilosophers2;

import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;

/**
 * @author Pavel Belevich
 *         Date: Jun 26, 2012
 *         Time: 1:50:50 PM
 */
public class Philosopher implements Runnable {

    public static final int THINKING = 0;
    public static final int WAITING = 1;
    public static final int EATING = 2;

    private static final Random r = new Random();
    public static final int period = 0;

    private int state = THINKING;

    private Fork left;
    private Fork right;

    private BlockingQueue<Philosopher> q;

    private CountDownLatch c;

    private String name;

    private boolean ready;

    public Philosopher(String name, BlockingQueue<Philosopher> q, CountDownLatch c) {
        this.name = name;
        this.q = q;
        this.c = c;
    }

    public boolean isReady() {
        return ready;
    }

    public void setReady(boolean ready) {
        this.ready = ready;
    }

    public Fork getLeft() {
        return left;
    }

    public void setLeft(Fork left) {
        this.left = left;
    }

    public Fork getRight() {
        return right;
    }

    public void setRight(Fork right) {
        this.right = right;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getState() {
        return state;
    }

    public void sleep() throws InterruptedException {
        //Thread.sleep(r.nextInt(period));
    }

    protected void waitForFork(Fork fork) {
        fork.setHolder(this);
    }

    protected void releaseFork(Fork fork) {
        fork.setHolder(null);
    }

    protected void iAmReady() throws InterruptedException {
        c.countDown();
        c.await();
    }

    protected void iAmWaiting() throws InterruptedException {
        synchronized (this) {
            wait();
        }
    }

    @Override
    public void run() {
        try {
            run0();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void run0() throws InterruptedException {
        iAmReady();
        while (true) {
            q.offer(this);
            synchronized (this) {
                this.setReady(true);
                notify();
                wait();
            }
            if (state == THINKING) {
                sleep();
                state = WAITING;
            } else if (state == WAITING) {
                synchronized (left) {
                    while (left.getHolder() != null) {
                        left.wait();
                    }
                    synchronized (right) {
                        while (right.getHolder() != null) {
                            right.wait();
                        }
                        left.setHolder(this);
                        right.setHolder(this);
                    }
                }
                state = EATING;
            } else if (state == EATING) {
                sleep();
                synchronized (left) {
                    synchronized (right) {
                        left.setHolder(null);
                        right.setHolder(null);
                        left.notify();
                        right.notify();
                    }
                }
                state = THINKING;
            }
        }
    }
}
